﻿using System;
using System.Collections.Generic;

namespace EventAggregator
{
    public sealed class EventHub : IDisposable
    {
        private readonly Dictionary<object, EventAggregator> _eventAggregators;
        private bool _breakEventLoop;
        private bool _isBroadcastingEvent;

        public EventHub()
        {
            _eventAggregators = new Dictionary<object, EventAggregator>();
        }

        #region IDisposable Members

        public void Dispose()
        {
            _eventAggregators.Clear();
        }

        #endregion

        public EventAggregator Resolve(object eventSource)
        {
            EventAggregator eventAggregator;

            if (_eventAggregators.ContainsKey(eventSource))
            {
                eventAggregator = _eventAggregators[eventSource];
            }
            else
            {
                eventAggregator = new EventAggregator(this);
                _eventAggregators.Add(eventSource, eventAggregator);
            }

            return eventAggregator;
        }

        public void Remove(EventAggregator eventAggregator)
        {
            _eventAggregators.Remove(eventAggregator);
        }

        public void BroadcastEvent(EventAggregator sender, EventMessage eventMessage)
        {
            _isBroadcastingEvent = true;

            var eventAggregators = new List<EventAggregator>(_eventAggregators.Values);

            foreach (EventAggregator p in eventAggregators)
            {
                if (p != sender && p.ContainsHandlers(eventMessage.Id))
                {
                    var eventHandlers = new List<EventMessageHandler>(p.GetEventHandlers(eventMessage.Id));

                    foreach (EventMessageHandler handler in eventHandlers)
                    {
                        if (_breakEventLoop)
                        {
                            break;
                        }

                        handler.HandleEvent(eventMessage);
                    }
                }

                if (_breakEventLoop)
                {
                    _breakEventLoop = false;
                    break;
                }
            }

            _isBroadcastingEvent = false;
        }

        public void BreakEventLoop()
        {
            if (!_isBroadcastingEvent)
            {
                throw new InvalidOperationException("No event loop to break!");
            }

            _breakEventLoop = true;
        }
    }
}